﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.Net;

namespace Pavilion.Communications
{
    public enum ConnectionStatus { Open, Idle, Closed }
    public abstract class BaseClient
    {
        public event Action<ConnectionStatus> ConnectionStatusChanged;

        public ConnectionStatus ConnectionStatus { get { return connectionStatus; } protected set { connectionStatus = value; OnConnectionStatusChanged(); } }
        
        protected ICommunicationObject proxy;
        private NetTcpBinding tcpBinding;
        private EndpointAddress endpointAddress;
        private ConnectionStatus connectionStatus;

        protected void OnConnectionStatusChanged()
        {
            if (ConnectionStatusChanged != null)
                ConnectionStatusChanged(ConnectionStatus);
        }

        protected abstract ICommunicationObject CreateProxy(NetTcpBinding tcpBinding, EndpointAddress endpointAddress);

        protected void Initialize(int port)
        {
            ConnectionStatus = ConnectionStatus.Closed;

            tcpBinding = new NetTcpBinding();
            tcpBinding.TransactionFlow = false;
            tcpBinding.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign;
            tcpBinding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows;
            tcpBinding.Security.Mode = SecurityMode.None;
            tcpBinding.MaxBufferSize = 2147483647;
            tcpBinding.MaxReceivedMessageSize = 2147483647;
            tcpBinding.MaxBufferPoolSize = 2147483647;
            tcpBinding.ReaderQuotas.MaxArrayLength = 2147483647;

            endpointAddress = new EndpointAddress("net.tcp://" + GetLocalIP().ToString() + ":" + port + "/Pavilion");
        }

        public void Disconnect()
        {
            if (ConnectionStatus != ConnectionStatus.Open)
                return;

            ConnectionStatus = ConnectionStatus.Idle;

            //System.Threading.Tasks.Task.Factory.StartNew(() =>
            //{
               
            //});

            CloseCommunication();

            ConnectionStatus = ConnectionStatus.Closed;
        }

        public void Connect()
        {
            if (ConnectionStatus == ConnectionStatus.Open)
                return;

            ConnectionStatus = ConnectionStatus.Idle;

            System.Threading.Tasks.Task.Factory.StartNew(() => OpenCommunication());
        }

        protected virtual void CloseCommunication()
        {
            try { proxy.Close(); }
            catch { }
        }

        protected virtual void OpenCommunication()
        {
            AttemptToConnect(20);
        }

        protected void AttemptToConnect(int attempts)
        {
            try
            {
                proxy = CreateProxy(tcpBinding, endpointAddress);
                proxy.Faulted += (sender, e) => ConnectionStatus = ConnectionStatus.Closed;
                proxy.Closed += (sender, e) => ConnectionStatus = ConnectionStatus.Closed;
                proxy.Opened += (sender, e) => ConnectionStatus = ConnectionStatus.Open;

                proxy.Open();
            }
            catch
            {
                if (attempts == 0)
                    Disconnect();
                else
                    AttemptToConnect(--attempts);
            }
        }

        private IPAddress GetLocalIP()
        {
            IPHostEntry host = Dns.GetHostEntry(Dns.GetHostName());

            foreach (IPAddress ip in host.AddressList)
            {
                if (ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                    return ip;
            }

            return null;
        }
    }
}
